#include <stdlib.h>
#include <chrono>
#include "all.hpp"
#include "log.h"
#include "default_interface.hpp"
#include "time_wheel.h"
#include "main_service.h"
#include "session.h"
#include "pipe_module.h"
#include "pipe.h"
#include "test_kcp.h"

// define sleep ms.
#define sleepMS(x) std::this_thread::sleep_for(std::chrono::milliseconds(x))

// a certain server class.
class CGameServer : public anet::pipe::IPipeMsgHandler {
public:
	CGameServer() : m_pipe(nullptr) {}
	virtual ~CGameServer() = default;

public:
	virtual void Handle(const char* msg, int len) override {
		Adebug("{}", std::string(msg, len));
	}

	void SetPipe(anet::pipe::IPipe* pipe) {
		m_pipe = pipe;
	}

	void SendMsg(const char* msg, int len) {
		if (m_pipe != nullptr) {
			m_pipe->Send(msg, len);
		}
	}

private:
	anet::pipe::IPipe* m_pipe;
};

// server manager class.
class CServerMgr : public anet::pipe::IPipeReporter {
public:
	CServerMgr() = default;
	virtual ~CServerMgr() = default;

public:
	virtual void OnReport(bool isConnected, anet::pipe::IPipe* pipe) {
		auto strId = anet::pipe::getPipeIdInfo(pipe->GetPipeId());
		if (isConnected) {
			Adebug("{}:({}) is connected", strId.c_str(), pipe->GetPipeId());

			pipe->SetMsgHandler(&m_server);
			m_server.SetPipe(pipe);

			// repeated timer.
			m_timer.add_repeated_timer([this](void* pData) {
				m_server.SendMsg("hello", 5);
			}, 0, 10);
		} else {
			m_timer.kill_timer(0);
			Adebug("{}:({}) is disconnected", strId.c_str(), pipe->GetPipeId());
			m_server.SetPipe(nullptr);
		}
	}

private:
	CGameServer m_server;
	STimeWheelSpace::CTimerRegister m_timer;
};

// test memory buckets.
static void TestMemoryBuckets() {
	SObjPoolSpace::memoryBuckets mb;
	for (int i = 0; i < 2; i++) {
		std::thread th([&mb] {
			for (;;) {
				auto size = rand() % 1024;
				char *p = mb.Alloc(size);
				mb.Dealloc(p);
				sleepMS(10);
			}
		});
		th.detach();
	}
	for (;;) {
		sleepMS(10000);
	}
}

int main(int argc, char* argv[]) {
	// init log module
	anet::log::initLog("./log", "test-net", anet::log::eLogLevel::debug, 1000);

	// rand seed.
	srand((unsigned int)(time(0)));

	// set log level.
	if (argc >= 2) {
		anet::log::setLogLevel(anet::log::eLogLevel(std::atoi(argv[1])));
	} else {
		anet::log::setLogLevel(anet::log::eLogLevel::debug);
	}

	// set start flag
	Adebug("{}", "=== start ===");

#if defined(_DEBUG)
	// debug mode
#if 1
	// http client test.
	if (!Connect("10.24.11.20", 12345)) {
		return -1;
	}

	for (;;) {
		KCPRun(1000);
		sleepMS(1);
	}

	/*
	// http synchronous session factory.
	auto sessionFactory = new anet::http::httSyncSessionFactory();
	int gSize = 10;
	for (int i = 0; i < gSize; i++) {
		auto thrd = std::make_unique<std::thread>([sessionFactory]() {
			anet::http::CHttpSyncClient client(sessionFactory);
			for (;;) {
				// get requirement
				auto getRet = client.get("10.24.11.20:8335", "/anet", "");
				AInfo("status:%d,content:%s", getRet.first, getRet.second.c_str());

				// post requirement
				auto postRet = client.post("10.24.11.20:8335", "/anet", "");
				AInfo("status:%d,content:%s", postRet.first, postRet.second.c_str());
				// sleepMS(1);
			}
		});
		thrd->detach();
	}
	for (;;) {
		sleepMS(1000);
	}

	// for the code's rightness.
	delete sessionFactory;
	*/

#else
	// client.
	CServerMgr *serverMgr = new CServerMgr();
	auto ret = anet::pipe::CPipeModule::instance()->Init("./pipe_config.xml", serverMgr);
	if (!ret) {
		delete serverMgr;
		return -1;
	}

	/*
	// tcp client.
	// anet::tcp::CEventLoop loop(2);
	// anet::tcp::CClient client(loop);
	anet::tcp::Client client(2);
	client.setPacketParser(new anet::tcp::CBigCodec());
	*/

	// Synchronous session.
	//auto session = new anet::tcp::CSyncSession();
	//client.setSession(session);
	//if (!client.syncConnect("10.24.10.37", 5566)) {
	//	return -1;
	//}

	/*
	for (;;) {
		auto session = new anet::tcp::CSession();
		client.setSession(session);
		while (client.syncConnect("10.24.11.20", 5566)) {
			break;
		}
		session->sendMsg(1, "hello");
		session->close();
		sleepMS(1);
	}
	*/

	/*
	long long id = 0;
	// try to send message
	auto send = [session,&id]() {
		std::string strIndex = std::to_string(id++);
		auto msgId = 1;
		auto &&msg = strIndex;
		session->Send(msgId, msg);
	};


	// thread and its execution body.
	std::thread th([session]() {
		long long id = 0;
		for (;;) {
			std::string strIndex = std::to_string(id++);
			auto res = session->require(1, strIndex);
			Ainfo("{}:{}", res.msgId, res.message.c_str());
			// sleepMS(1);
		}
	});
	th.detach();

	// main thread to do it.
	for (;;) {
		send();
		sleepMS(1);
	}
	*/

	// main thread run.
	bool busy = false;
	int runCount = 10000;
	for (;;) {
		busy = false;
		busy = anet::pipe::CPipeModule::instance()->Run(runCount);
		if (!busy) {
			sleepMS(1);
		}
	}

#endif
#else
	// release mode
    // server 
#if 0
    CServerMgr* serverMgr = new CServerMgr();
	auto ret = anet::pipe::CPipeModule::instance()->Init("./pipe_config.xml", serverMgr);
	if (!ret) {
		delete serverMgr;
		return -1;
	}

	// tcp server.
	// anet::tcp::CEventLoop loop(2);
	// anet::tcp::CServer server(loop);
	anet::tcp::Server server(3);
	server.proxy().setPacketParser(new anet::tcp::CBigCodec());
	// server.proxy().setSessionFactory(new anet::tcp::CSessionFactory());

	// set session factory.
	using userSessionFactoryType = anet::tcp::CSharePtrSessionFactory<CClientSession>;
	auto sessionFactory = new userSessionFactoryType();
	server.proxy().setSessionFactory(sessionFactory);

	// start listen.
	if (!server.proxy().start("0", 5566)) {
		return -1;
	}

	// main thread run.
	bool busy = false;
	int runCount = 10000;
	for (;;) {
		busy = false;
		busy = anet::pipe::CPipeModule::instance()->Run(runCount);
		if (!busy) {
			sleepMS(1);
		}
	}
#else
     if (!Start("0", 12345)) {
	    return -1;
	 }
     for (;;) {
	    KCPRun(1000);
	    sleepMS(1);
     }
   /*
	// http server
	anet::http::CHttpServer server(2);
	// == define all kinds of post interfaces.
	server.post("/anet", [](const anet::http::HttpRes& res) ->anet::http::httpResPair {
		return { anet::http::httpStatus::OK, std::move(std::string("hello,post anet")) };
	});
	server.get("/anet", [](const anet::http::HttpRes& res) ->anet::http::httpResPair {
		return { anet::http::httpStatus::OK, std::move(std::string("hello,get anet")) };
	});
	server.start("0", 8335);
	for (;;) {
		sleepMS(1000);
	}
	*/
#endif
#endif
	// release log.
	anet::log::releaseLog();

	return 0;
}
